package service.impl;

import com.google.common.collect.ImmutableList;
import common.consts.Accounts;
import common.consts.Consts;
import common.consts.Offers;
import common.consts.Recruitments;
import dao.*;
import domain.Account;
import domain.Offer;
import domain.PushMessage;
import domain.Recruitment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import service.EmployService;
import service.ServiceResponse;
import service.util.MessagePushUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by aceyin on 15/6/30.
 */
@Service
public class EmployServiceImpl extends ServiceBase implements EmployService {
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private PushMessageDao pushMessageDao;
    @Autowired
    private OfferDao offerDao;
    @Autowired
    private UserDataProfileDao dataProfileDao;
    @Autowired
    private RecruitmentDao recruitmentDao;
    @Autowired
    private WorkExperienceDao workExperienceDao;

    /**
     * 录用所有应聘者，在数据合法的情况下：
     * <pre>
     * 1. 更新对应Offer的状态为：EMPLOYED
     * 2. 更新UserDataProfile里面的数据：employed_quantity
     * 3. 给用户发送Push消息
     * </pre>
     *
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public ServiceResponse employAll(long employer, long recruitmentId) {
        Recruitment recruitment = recruitmentDao.get(recruitmentId);
        ServiceResponse response = validateRecruitment(recruitment);
        if (response.isFailed()) {
            return response;
        }

        long owner = recruitment.getOwner();
        if (owner != employer) {
            return ServiceResponse.fail(Consts.i18nKey.INVALID_REQUEST);
        }

        // 更新所有该工作的offer的状态
        List<Offer> offers = offerDao.findRecruitmentOffersByStatus(recruitmentId, Offers.Status.WAITING.name());

        if (offers == null || offers.isEmpty()) {
            return ServiceResponse.success();
        }

        handleOffers(recruitment, offers);

        return ServiceResponse.success();
    }

    @Override
    @Transactional(readOnly = false)
    public ServiceResponse employ(long employer, long applier, long recruitmentId) {
        Recruitment recruitment = recruitmentDao.get(recruitmentId);
        ServiceResponse response = validateRecruitment(recruitment);
        if (response.isFailed()) {
            return response;
        }

        long owner = recruitment.getOwner();
        if (owner != employer) {
            return ServiceResponse.fail(Consts.i18nKey.INVALID_REQUEST);
        }

        Account account = accountDao.get(applier);
        if (account == null) {
            return ServiceResponse.fail(Consts.i18nKey.UNKNOWN_USER);
        }
        if (Accounts.Status.isAbnormal(account.getStatus())) {
            return ServiceResponse.fail(Consts.i18nKey.ACCOUNT_IS_ABNORMAL);
        }

        Offer offer = offerDao.findOffer(recruitmentId, applier);
        if (offer == null) {
            ServiceResponse.fail(Consts.i18nKey.UNKNOWN_OFFER);
        }
        if (Offers.isEmployed(offer.getStatus())) {
            return ServiceResponse.fail(Consts.i18nKey.APPLIER_HAS_BEEN_EMPLOYED);
        }
        List<Offer> offers = ImmutableList.<Offer>builder().add(offer).build();
        handleOffers(recruitment, offers);
        return ServiceResponse.success();
    }

    private void handleOffers(Recruitment recruitment, List<Offer> offers) {
        List<Long> recruitedOfferIds = new ArrayList<>();
        List<Long> recruitedAppliers = new ArrayList<>();

        List<Long> canceledOfferIds = new ArrayList<>();
        List<Long> canceledAppliers = new ArrayList<>();

        int recruited = recruitment.getRecruitQuantity();
        int required = recruitment.getQuantity();
        int leftNum = required - recruited;

        for (Offer offer : offers) {
            Long id = offer.getId();
            long applier = offer.getApplier();
            if (leftNum > 0) {
                recruitedOfferIds.add(id);
                recruitedAppliers.add(applier);
                leftNum--;
                recruited++;
            } else {
                canceledOfferIds.add(id);
                canceledAppliers.add(applier);
            }
        }

        // 更新 recruitment 的 recruit_quantity属性
        recruitmentDao.updateRecruitQuantity(recruitment.getId(), recruited);

        Account employer = accountDao.get(recruitment.getOwner());
        List<PushMessage> messages = new ArrayList<>();

        /*-------------------part1------------------*/
        // 更新已经被录用的offer的状态
        offerDao.updateOfferStatus(Offers.Status.EMPLOYED.name(), recruitedOfferIds);
        // 更新用户的 data profile，将recruit_quantity 加一
        dataProfileDao.increaseEmployedQuantity(recruitedAppliers);
        // 生成录用消息
        for (Long applier : recruitedAppliers) {
            PushMessage message = MessagePushUtil.createEmployMessage(employer.getNick(), applier);
            messages.add(message);
        }
        // 增加用户的工作经验
        String typeCode = recruitment.getTypeCode();
        List<Long> userIdHasExperience = workExperienceDao.findUserHasExperience(recruitedAppliers, typeCode);
        if (userIdHasExperience != null && !userIdHasExperience.isEmpty()) {
            workExperienceDao.increaseExperience(userIdHasExperience, typeCode);
            recruitedAppliers.removeAll(userIdHasExperience);
        }
        workExperienceDao.addExperience(recruitedAppliers, typeCode);

        /*-------------------part2------------------*/
        // 更新被取消的offer状态
        offerDao.updateOfferStatus(Offers.Status.CANCELED.name(), canceledOfferIds);
        // 生成取消消息
        for (Long applier : canceledAppliers) {
            PushMessage message = MessagePushUtil.createFullMessage(employer.getNick(), applier);
            messages.add(message);
        }

        // 群发消息
        pushMessageDao.batchInsert(messages);

    }

    private ServiceResponse validateRecruitment(Recruitment recruitment) {
        if (recruitment == null) {
            return ServiceResponse.fail(Consts.i18nKey.UNKNOWN_RECRUITMENT);
        }

        String status = recruitment.getStatus();
        if (!Recruitments.Status.isNormal(status)) {
            return ServiceResponse.fail(Consts.i18nKey.RECRUITMENT_IS_ABNORMAL);
        }

        int required = recruitment.getQuantity();
        Integer recruited = recruitment.getRecruitQuantity();
        if (recruited != null && recruited.intValue() >= required) {
            return ServiceResponse.fail(Consts.i18nKey.RECRUITMENT_HAS_FULLED);
        }
        return ServiceResponse.success();
    }

    public void setWorkExperienceDao(WorkExperienceDao workExperienceDao) {
        this.workExperienceDao = workExperienceDao;
    }

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void setPushMessageDao(PushMessageDao pushMessageDao) {
        this.pushMessageDao = pushMessageDao;
    }

    public void setOfferDao(OfferDao offerDao) {
        this.offerDao = offerDao;
    }

    public void setDataProfileDao(UserDataProfileDao dataProfileDao) {
        this.dataProfileDao = dataProfileDao;
    }

    public void setRecruitmentDao(RecruitmentDao recruitmentDao) {
        this.recruitmentDao = recruitmentDao;
    }
}
